﻿using System;
using System.Dynamic;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Ecobee.Net
{
	public enum ModelNumber
	{
		None,
		IdtSmart,
		IdtEms,
		SiSmart,
		SiEms
	}

	public class Thermostat : ApiObject
	{
		private string _identifier = string.Empty;
		private string _name = string.Empty;
		private bool _connected = false;

		private Runtime _runtime = null;
		private Settings _settings = null;
		private EventList _eventList = null;
		private AlertList _alertList = null;

		private Revision _thermostatRevision = null;
		private Revision _alertsRevision = null;
		private Revision _runtimeRevision = null;

		private ModelNumber _modelNumber = ModelNumber.None;

		public Thermostat()
		{
			this.Runtime = new Runtime();
			this.Settings = new Settings();
			this.Events = new EventList("Events");
			this.Alerts = new AlertList("Alerts");

			this.ThermostatRevision = new Revision();
			this.AlertsRevision = new Revision();
			this.RuntimeRevision = new Revision();
		}

		/// <summary>
		/// The thermostat identifier.
		/// </summary>
		public string Identifier
		{
			get
			{
				return _identifier;
			}
			set
			{
				this.SetProperty(ref _identifier, value);
			}
		}

		/// <summary>
		/// The thermostat name, otherwise an empty field if one is not set.
		/// </summary>
		public string Name
		{
			get
			{
				return _name;
			}
			set
			{
				this.SetProperty(ref _name, value);
			}
		}

		/// <summary>
		/// Whether the thermostat is currently connected to the ecobee servers.
		/// </summary>
		public bool Connected
		{
			get
			{
				return _connected;
			}
			set
			{
				this.SetProperty(ref _connected, value);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public ModelNumber ModelNumber
		{
			get
			{
				return _modelNumber;
			}
			set
			{
				this.SetProperty(ref _modelNumber, value);
			}
		}

		/// <summary>
		/// Current thermostat revision. This revision is incremented whenever the thermostat program, HVAC mode, 
		/// settings or configuration change.
		/// </summary>
		public Revision ThermostatRevision
		{
			get
			{
				return _thermostatRevision;
			}
			set
			{
				this.SetProperty(ref _thermostatRevision, value);
			}
		}

		/// <summary>
		/// Current revision of the thermostat alerts. This revision is incremented whenever a new alert is issued or 
		/// an alert is modified (acknowledged or deferred).
		/// </summary>
		public Revision AlertsRevision
		{
			get
			{
				return _alertsRevision;
			}
			set
			{
				this.SetProperty(ref _alertsRevision, value);
			}
		}

		/// <summary>
		/// The current revision of the thermostat runtime settings. This revision is incremented whenever the 
		/// thermostat transmits a new status message. The thermostat does this on a 15 minute interval.
		/// </summary>
		public Revision RuntimeRevision
		{
			get
			{
				return _runtimeRevision;
			}
			set
			{
				this.SetProperty(ref _runtimeRevision, value);
			}
		}

		/// <summary>
		/// The runtime object represents the last known thermostat running state. This state is composed from 
		/// the last interval status message received from a thermostat. It is also updated each time the 
		/// thermostat posts configuration changes to the server. The runtime object is read-only.
		/// </summary>
		public Runtime Runtime
		{
			get
			{
				return _runtime;
			}
			set
			{
				this.SetProperty(ref _runtime, value);
			}
		}

		/// <summary>
		/// The Settings contains all the configuration properties of the thermostat.
		/// </summary>
		public Settings Settings
		{
			get
			{
				return _settings;
			}
			set
			{
				this.SetProperty(ref _settings, value);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public EventList Events
		{
			get
			{
				return _eventList;
			}
			set
			{
				this.SetProperty(ref _eventList, value);
			}
		}

		/// <summary>
		/// The thermostat's unacknowledged Alert objects.
		/// </summary>
		public AlertList Alerts
		{
			get
			{
				return _alertList;
			}
			set
			{
				this.SetProperty(ref _alertList, value);
			}
		}

		#region Actions
		public async Task<bool> LoadDetails(Access access, Selection selection)
		{
			bool returnValue = false;

			// ***
			// *** Always override these properties so that
			// *** only the the data for the current thermostat
			// *** is retrieved.
			// ***
			selection.SelectionType = SelectionType.Thermostats;
			selection.SelectionMatch = this.Identifier;

			// ***
			// *** A Thermostats object is required to request
			// *** details for one or more thermostats.
			// ***
			ThermostatUpdate thermostatUpdate = new ThermostatUpdate()
			{
				Selection = selection
			};

			// ***
			// *** Call the web service
			// ***
			ApiResult response = await Api.Http.Get(Api.KnownUri.Thermostat(thermostatUpdate), access);

			if (response.Successful && response.DataObject != null)
			{
				if (response.DataObject.thermostatList != null)
				{
					if (selection.IncludeRuntime)
					{
						this.Runtime.FromDataObject(response.DataObject.thermostatList[0].runtime);
					}

					if (selection.IncludeSettings)
					{
						this.Settings.FromDataObject(response.DataObject.thermostatList[0].settings);
					}

					if (selection.IncludeEvents)
					{
						this.Events.Load(response.DataObject.thermostatList[0].events);
					}

					if (selection.IncludeAlerts)
					{
						this.Alerts.Load(response.DataObject.thermostatList[0].alerts);
					}
				}
			}

			return returnValue;
		}

		internal static async Task<dynamic> LoadMultipleDetails(Access access, Selection selection)
		{
			dynamic returnValue = null;

			// ***
			// *** A Thermostats object is required to request
			// *** details for one or more thermostats.
			// ***
			ThermostatUpdate thermostatUpdate = new ThermostatUpdate()
			{
				Selection = selection
			};

			// ***
			// *** Call the web service
			// ***
			ApiResult response = await Api.Http.Get(Api.KnownUri.Thermostat(thermostatUpdate), access);

			if (response.DataObject.thermostatList != null)
			{
				returnValue = response.DataObject;
			}

			return returnValue;
		}

		public async Task<bool> AcknowledgeAlert(Access access, Alert alert, AcknowledgeType acknowledgeType)
		{
			bool returnValue = false;

			// ***
			// *** Build the data structure to call Acknowledge in the API
			// ***
			FunctionList functions = new FunctionList()
			{
				Selection = new Selection()
				{
					SelectionType = SelectionType.Thermostats,
					SelectionMatch = this.Identifier
				},
				Functions = new Function[] { Function.Acknowledge(
													new Acknowledge()
													{
														ThermostatIdentifier = this.Identifier,
														AcknowledgeRef = alert.AcknowledgeRef,
														AcknowledgeType = acknowledgeType,
														RemindMeLater = (acknowledgeType == AcknowledgeType.Defer)
													})
												}
			};

			ApiResult response = await Api.Http.Post(Api.KnownUri.Thermostat(), access, functions);

			// ***
			// *** Check if the call was successful
			// ***
			if (response.Successful)
			{
				// ***
				// *** Remove this from the collection; it will be gone in the
				// *** next update for this thermostat
				// ***
				this.Alerts.Remove(alert);
				returnValue = true;
			}

			return returnValue;
		}

		public async Task<bool> SendMessage(Access access, string text)
		{
			bool returnValue = false;

			// ***
			// *** Build the data structure to call Acknowledge in the API
			// ***
			FunctionList functions = new FunctionList()
			{
				Selection = new Selection()
				{
					SelectionType = SelectionType.Thermostats,
					SelectionMatch = this.Identifier
				},
				Functions = new Function[] { Function.SendMessage(
													new Message()
													{
														Text = text.Limit(500)
													})
												}
			};

			ApiResult response = await Api.Http.Post(Api.KnownUri.Thermostat(), access, functions);

			returnValue = response.Successful;

			return returnValue;
		}
		#endregion

		/// <summary>
		/// Returns a string that represents the current object
		/// that can be used for display purposes.
		/// </summary>
		/// <returns>A string that represents the current object.</returns>
		public override string ToString()
		{
			return this.Name;
		}

		protected override string OnUniqueKey()
		{
			return this.Identifier;
		}

		protected override string OnGetKeyValue(dynamic dataObject)
		{
			return dataObject.identifier;
		}
	}
}
